package com.zte.claa.inficombo.csa.app.service.csa.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.script.Invocable;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import com.zte.claa.inficombo.csa.app.comm.csif.CSIFCmdBuilder;
import com.zte.claa.inficombo.csa.app.comm.csif.CSIFCommService;
import com.zte.claa.inficombo.csa.app.comm.csif.CSIFCommServiceImpl;
import com.zte.claa.inficombo.csa.app.comm.csif.CSIFCommand;
import com.zte.claa.inficombo.csa.app.config.CSAConfigProps;
import com.zte.claa.inficombo.csa.app.config.CSIFCmdCodeProps;
import com.zte.claa.inficombo.csa.app.config.CSIFCommProps;
import com.zte.claa.inficombo.csa.app.config.RedisChannelProps;
import com.zte.claa.inficombo.csa.app.controller.ret.AppStatus;
import com.zte.claa.inficombo.csa.app.dao.mysql.impl.AppMapper;
import com.zte.claa.inficombo.csa.app.dao.mysql.impl.ClaaDevInitStateMapper;
import com.zte.claa.inficombo.csa.app.dao.mysql.impl.DevCmdConfDataMapper;
import com.zte.claa.inficombo.csa.app.model.app.AppTarget;
import com.zte.claa.inficombo.csa.app.model.dev.ClaaDevInitState;
import com.zte.claa.inficombo.csa.app.model.dev.DevBase;
import com.zte.claa.inficombo.csa.app.model.dev.DevPagePara;
import com.zte.claa.inficombo.csa.app.model.dev.ResourceShadow;
import com.zte.claa.inficombo.csa.app.service.csa.CSAMainService;
import com.zte.claa.inficombo.csa.app.service.dev.DevBaseService;
import com.zte.claa.inficombo.csa.app.service.gprs.bean.GPRSDevCommand;
import com.zte.claa.inficombo.csa.app.service.gprs.mina.GPRSDevConnSession;
import com.zte.claa.inficombo.csa.app.service.gprs.mina.GPRSDevServerManager;
import com.zte.claa.inficombo.csa.app.service.jsplugin.CSAJSPluginService;
import com.zte.claa.inficombo.csa.app.service.jsplugin.JSParam;
import com.zte.claa.inficombo.csa.app.service.nbiot.NBIotCmdAck;
import com.zte.claa.inficombo.csa.app.service.nbiot.NBIotDevDataRptService;
import com.zte.claa.inficombo.csa.app.service.redis.CSAMessagePublisher;
import com.zte.claa.inficombo.csa.app.service.rescmd.ResDevCmdParser;
import com.zte.claa.inficombo.csa.app.service.rescmd.impl.ResCommandContainer;
import com.zte.claa.inficombo.csa.app.service.rptparse.ParserContainer;
import com.zte.claa.inficombo.csa.app.util.JsonUtils;
import com.zte.claa.inficombo.csa.app.util.SecurityUtil;

@Service
public class CSAMainServiceImpl implements CSAMainService, InitializingBean, DisposableBean {
	// sensor dev的命令参数slotid名称
	private static final String CMD_PARA_SLOTID = "slotid";
	// sensor dev的命令参数addr名称（主要针对modbus类设备）
	private static final String CMD_PARA_ADDR = "addr";
	
	// 日志
	private Logger logger = LoggerFactory.getLogger(CSAMainServiceImpl.class);
	
	@Autowired
	private CSIFCmdCodeProps csifCmdCodeProps;
	
	@Autowired
	private CSIFCommProps csifCommProps;
	
	@Autowired
	private CSAConfigProps csaConfProps;
	
	@Autowired
	private AppMapper appMapper;
	
	@Autowired
	private ParserContainer rptParserContainer;
	
	@Autowired
	private StringRedisTemplate redisStrTemplate;
	
	@Autowired
	private RedisChannelProps redisChannelProps;
	
	@Autowired
	private CSAJSPluginService jsPnpService;
	
	@Autowired
	private BaseCacheManager cacheManager;
	
	@Autowired
	private DevCmdConfDataMapper devCmdConfMapper;
	
	@Autowired
	private com.zte.claa.inficombo.csa.app.service.gprs.GPRSDevService gprsDevService;
	
	@Autowired
	private com.zte.claa.inficombo.csa.app.service.gprscat1.GPRSDevService gprscat1DevService;
		
    @Autowired 
    private DevBaseService devBaseService;
    
	@Autowired
	private ClaaDevInitStateMapper devInitStateMapper;
	
	@Autowired
	private NBIotDevDataRptService nbiotService;

	@Autowired
	private ResCommandContainer resCmdContainer;
	
    
	// redis消息发布器.-->日志上报数据发布；
	private CSAMessagePublisher logPublisher;
	
	// CSIF连接重连管理器.
	private CSIFReConnManager csifConnManager;
	
	// 上报数据监听器.
	private DataReportManager reportManager;
	
	/**
	 * appEUI到CSIFCommService的映射表；
	 * key: appEUI，还可以用于检测当前CSA服务实例支持的AppEUI列表；
	 * val: CSIFCommService；
	 */
	private Map<String, CSIFCommService> appeui2CSIFMap = new ConcurrentHashMap<String, CSIFCommService>();

	/**
	 * 服务初始化.
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		this.logPublisher = new CSAMessagePublisher(this.redisStrTemplate, this.redisChannelProps.getLogrptchannel().trim());
		this.csifConnManager = new CSIFReConnManager(this.appeui2CSIFMap);
		this.csifConnManager.setLogPublisher(this.logPublisher);
		this.reportManager = new DataReportManager(this.csaConfProps, this.appeui2CSIFMap);
		this.reportManager.setParserContainer(this.rptParserContainer);
		// 从数据库读取本服务实例名对应的AppEUI路由记录，进行CSIF建链和appEUI认证，异步线程模式....
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				// 推迟10秒加载数据...
				try{
					Thread.sleep(10*1000L);
				}catch(Exception e){}
				loadAppEUIDataFromDB();
			}
    	});
    	t1.start();
    	logger.info("Workers for loadAppEUIDataFromDB will be started in 10 seconds...");		
	}
	
	/**
	 * 从数据库加载本CSA服务实例关联的APPEUI数据
	 * 
	 */
	private void loadAppEUIDataFromDB(){
		try{
			// 根据csaConfProps的workmode来判断...
			List<AppTarget> appList = null;
			if(this.csaConfProps.isSingleMode()){
				appList = this.appMapper.queryAllAppTargets();
			} else {
				int csaSvrId = this.csaConfProps.getSvrinstanceid();
				String csaSericeName = this.csaConfProps.getServicename();
				appList = this.appMapper.queryAppTargets(csaSericeName, csaSvrId);
			}
			if(appList == null || appList.size() == 0){
				logger.warn("Current work mode="+this.csaConfProps.getWorkmode()+", no appeui to be loaded!");
				return;
			}
			for(AppTarget app : appList){
				try{
					String decodeKey = SecurityUtil.AESCbcDecrypt(app.getAppAuthKey());
					app.setAppAuthKey(decodeKey);
					this.addAppEui(app.getAppEUI(), app);
				}catch(Exception ex){
					logger.error("addAppEui failed: "+app.getAppEUI(), ex);
				}
			}
			logger.info("OK, finish load appeui from db: totalnum="+appList.size());
		}catch(Exception e){
			logger.error("Load appeui from db failed!", e);
		}
	}
	
	/**
	 * 系统注销
	 * 
	 */
	@Override
	public void destroy() throws Exception {
		if(this.csifConnManager != null){
			this.csifConnManager.shutdown();
			this.csifConnManager = null;
		}
		if(this.reportManager != null){
			this.reportManager.shutdown();
			this.reportManager = null;
		}
		// 释放CSIF连接		
		Iterator<String> it = this.appeui2CSIFMap.keySet().iterator();
		while(it.hasNext()){
			String appEui = it.next();
			CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
			ccs.closeConnectToCSIF();
			it.remove();
		}		
	}

	/**
	 * 添加appEUI
	 * 主要是web服务器上开通新appEUI时触发.
	 */
	@Override
	public void addAppEui(String appeui, AppTarget appTarget) throws Exception {
		if(appeui == null || appTarget == null){
			return;
		}
		// 检查appEUI是否已经存在.
		CSIFCommService ccs = this.appeui2CSIFMap.get(appeui);
		if(ccs != null){
			logger.warn("AppEUI has already exist: "+appeui);
			if(!ccs.hasConnectToCSIF()){
				ccs.connectToCSIF();
				// ccs.authToCSIF();  // 这里采用异步join的模式，加快断链重连的效率，避免拥塞.
				ccs.authToCSIFByAsynMode();
			}
			return;
		}
		ccs = new CSIFCommServiceImpl(appTarget, this.csifCommProps);
		ccs.setReconnectListener(this.csifConnManager);
		ccs.setReportDataListener(this.reportManager);
		ccs.setCSIFCmdCodeProps(this.csifCmdCodeProps);
		if(this.csaConfProps.checkPublishRspLog()){
			ccs.setLogPublisher(this.logPublisher);
		}
		// 先添加到映射表中.
		this.appeui2CSIFMap.put(appeui, ccs);
		ccs.connectToCSIF();
		// ccs.authToCSIF();
		// 这里采用异步join的模式，加快断链重连的效率，避免拥塞.
		ccs.authToCSIFByAsynMode();
		logger.info("addAppEui ok: appeui="+appeui+", CSIFIP="+appTarget.getCsifIpAddr());
	}

	/**
	 * 删除appeui。
	 * 两种调用场景：
	 * 1）appeui注销；
	 * 2）appeui被迁移到其他csa服务器（多csa场景下）；
	 */
	@Override
	public void removeAppEui(String appeui) throws Exception {
		if(appeui == null){
			return;
		}
		CSIFCommService ccs = this.appeui2CSIFMap.get(appeui);
		if(ccs == null){
			logger.warn("AppEUI does NOT exist in current CSA instance: "+appeui);
			return;
		}
		ccs.closeConnectToCSIF();
		this.appeui2CSIFMap.remove(appeui);
		logger.info("Remove appeui from current CSA instance ok: "+appeui);
	}

	/**
	 * 查询当前csa服务器实例支持的AppEUI列表
	 * 
	 */
	@Override
	public List<String> getAppEuis() {
		Set<String> appEuiSet = this.appeui2CSIFMap.keySet();
		List<String> ret = new ArrayList<String>();
		ret.addAll(appEuiSet);
		return ret;
	}
	
	/**
	 * 返回appeui对应的apptarget
	 */
	public AppTarget getAppTarget(String appeui){
		if(appeui == null || appeui.trim().equals("")){
			return null;
		}
		// 检查appEUI是否已经存在.
		CSIFCommService ccs = this.appeui2CSIFMap.get(appeui);
		if(ccs != null){
			return ccs.getAppTarget();
		}
		return null;
	}
	
	// 查询到CSIF的链路状态.
	public AppStatus getAppLinkState(String appeui) {
		AppStatus st = new AppStatus();
		st.setAppEui(appeui);
		st.setLinkStatus(AppStatus.LINK_STATUS_UNNKNOWN);
		if(appeui == null || appeui.trim().equals("")){
			return st;
		}
		// 检查appEUI是否已经存在.
		CSIFCommService ccs = this.appeui2CSIFMap.get(appeui);
		if(ccs != null){
			if(ccs.hasConnectToCSIF()){
				st.setLinkStatus(AppStatus.LINK_STATUS_CONN);
			} else {
				st.setLinkStatus(AppStatus.LINK_STATUS_CLOSE);
			}
		}
		return st;
	}

	/**
	 * cs join
	 */
	@Override
	public String CSJoin(String appEui) throws Exception {
		logger.info("CSJoin: appEUI="+appEui);
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if(ccs == null){
			throw new Exception("AppEUI not exist in cur CSA module: "+appEui);
		}
		if(ccs.hasConnectToCSIF() == false){
			ccs.connectToCSIF();
		}
		return ccs.authToCSIF();
	}

	/**
	 * cs quit
	 */
	@Override
	public String CSQuit(String appEui) throws Exception {
		logger.info("CSQuit: appEUI=" + appEui);
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if (ccs == null) {
			throw new Exception("AppEUI not exist in cur CSA module: " + appEui);
		}
		String rsp = "{}";
		try {
			CSIFCommand command = CSIFCmdBuilder.buildCSQuitCmd();
			rsp = ccs.execCommand(command);
			logger.info("Send CSQuit cmd OK: " + ccs.getAppTarget());
		} catch (Exception e) {
			logger.error("Send CSQuit cmd FAILED: " + ccs.getAppTarget(), e);
			StringBuffer sb = new StringBuffer();
			sb.append("{");
			sb.append("\"cmd\":\"quit_ack\", ");
			sb.append("\"appeui\":\"").append(appEui).append("\", ");
			sb.append("\"code\": 406, ");
			sb.append("\"msg\": \"").append(e.getMessage()).append("\"");
			sb.append("}");
			rsp = sb.toString();		
		} finally {
			ccs.closeConnectToCSIF();
		}
		return rsp;
	}

	private boolean checkGprsChannelValid(String dtueui){
		if(null == dtueui){
			return false;
		}
		GPRSDevServerManager devsvr = this.gprsDevService.getConnSvrManager();
		if(null == devsvr){
			return false;
		}
		Map<String, GPRSDevConnSession> sessionmap = devsvr.getDeveuiSessionMap();
		if(null == sessionmap){
			return false;
		}
		if(null != sessionmap.get(dtueui)){
			return true;
		}
		return false;
	}
	
	private boolean checkGprsCat1ChannelValid(String dtueui){
		if(null == dtueui){
			return false;
		}
		com.zte.claa.inficombo.csa.app.service.gprscat1.mina.GPRSDevServerManager devsvr = this.gprscat1DevService.getConnSvrManager();
		if(null == devsvr){
			return false;
		}
		Map<String, com.zte.claa.inficombo.csa.app.service.gprscat1.mina.GPRSDevConnSession> sessionmap = devsvr.getDeveuiSessionMap();
		if(null == sessionmap){
			return false;
		}
		if(null != sessionmap.get(dtueui)){
			return true;
		}
		return false;
	}
	
	/**
	 * dev 控制命令下发.
	 * payload -->符合设备控制模型的json字符串： {"cmd":"cmdname", "paras":{"p1":"v1", "p2":"v2", ...}} 
	 */
	@Override
	public String SendDevCtrlCmd(String appEui, String devEui, boolean confirm, int port, String payload, Map<String, Object> extParaMap) throws Exception {
		String retStr = "";
		DevBase devbase = this.cacheManager.getDev(devEui);
		// 如果devbase不存在，则进行逻辑资源相关处理
		if(null == devbase){
			ResourceShadow resShadow = this.cacheManager.getRes(devEui);
			if(null != resShadow){
				ResDevCmdParser resparser = this.resCmdContainer.getParserByResDevtype(resShadow.getRestype());
				resparser.dealResDevCmdFunc(appEui, devEui, confirm, port, payload, extParaMap);
				retStr = "{\"code\":200}";
				return retStr;
			}
		}
		if(devbase.checkNBIotDev()){
			boolean offlineMode = !confirm;
			NBIotCmdAck ack = this.nbiotService.execNBIotCmd(devbase, payload, extParaMap, offlineMode); 
			int comboRspCode = (ack.getErrno() == NBIotCmdAck.ACK_OK) ? 200 : 500;
			JSONObject ackJson = new JSONObject();
			ackJson.put("deveui", devEui);
			ackJson.put("code", comboRspCode);
			ackJson.put("msg", ack.getError()+"("+ack.getErrno()+").");
			return ackJson.toString();
		}
		String dtueui = devbase.parseDtuDevEui();
		if(true == this.checkGprsChannelValid(dtueui))
		{
			//处理GPRS侧命令	
			logger.info("GPRS SendDevCtrlCmd: devEui="+devEui);
			GPRSDevCommand gprscmd = this.BuildGPRSDevCtrlCmd(appEui, devEui, confirm, port, payload, extParaMap);
			gprscmd.setCmdtype((byte)GPRSDevCommand.GPRS_DOWNLINK);			
			this.gprsDevService.sendCommand(dtueui, gprscmd);
			retStr = "{\"code\":200}";
		}else if(true == this.checkGprsCat1ChannelValid(dtueui)){
			//处理GPRS侧命令	
			logger.info("GPRSCat1 SendDevCtrlCmd: devEui="+devEui);
			com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSDevCommand gprscmd = this.BuildGPRSCat1DevCtrlCmd(appEui, devEui, confirm, port, payload, extParaMap);		
			this.gprscat1DevService.sendCommand(dtueui, gprscmd);
			retStr = "{\"code\":200}";
		}
		else{
			logger.info("LoRa SendDevCtrlCmd: appEUI="+appEui+", payload="+payload);
    		CSIFCommand command = this.BuildDevCtrlCmd(appEui, devEui, confirm, port, payload, extParaMap);
    		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
    		retStr = ccs.execCommand(command);
		}
		return retStr;
	}
	
	/**
	 * dev 控制命令下发.
	 * payload -->符合设备控制模型的json字符串： {"cmd":"cmdname", "paras":{"p1":"v1", "p2":"v2", ...}} 
	 */
	@Override
	public void SendDevCtrlCmdIgnoreRsp(String appEui, String devEui, boolean confirm, int port, String payload, Map<String, Object> extParaMap) throws Exception {
		DevBase devbase = this.cacheManager.getDev(devEui);	
		if(null == devbase){
			logger.info("Device is not existed,devEui:"+devEui);
			ResourceShadow resShadow = this.cacheManager.getRes(devEui);
			if(null != resShadow){
				ResDevCmdParser resparser = this.resCmdContainer.getParserByResDevtype(resShadow.getRestype());
				resparser.dealResDevCmdFunc(appEui, devEui, confirm, port, payload, extParaMap);
			}
			return;
		}
		if(devbase.checkNBIotDev()){
			boolean offlineMode = !confirm;
			this.nbiotService.execNBIotCmd(devbase, payload, extParaMap, offlineMode);
			return;
		}
		String dtueui = devbase.parseDtuDevEui();
		if(null == dtueui){
			logger.info("Device DTU is not existed,dtuEui:"+dtueui);
			return;
		}
		if(true == this.checkGprsChannelValid(dtueui))
		{
			//处理GPRS侧命令
			logger.info("GPRS SendDevCtrlCmdIgnoreRsp: devEui="+devEui);
			GPRSDevCommand gprscmd = this.BuildGPRSDevCtrlCmd(appEui, devEui, confirm, port, payload, extParaMap);
			gprscmd.setCmdtype((byte)GPRSDevCommand.GPRS_DOWNLINK);
			this.gprsDevService.sendCommand(devbase.parseDtuDevEui(), gprscmd);
		}else if(true == this.checkGprsCat1ChannelValid(dtueui)){
			//处理GPRSCat1侧命令	
			logger.info("GPRSCat1 SendDevCtrlCmd: devEui="+devEui);
			com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSDevCommand gprscat1cmd = this.BuildGPRSCat1DevCtrlCmd(appEui, devEui, confirm, port, payload, extParaMap);		
			this.gprscat1DevService.sendCommand(dtueui, gprscat1cmd);			
		}
		else{
			logger.info("LoRa SendDevCtrlCmdIgnoreRsp: appEUI="+appEui+", payload="+payload);
			CSIFCommand command = this.BuildDevCtrlCmd(appEui, devEui, confirm, port, payload, extParaMap);
			CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
			ccs.execCommandIngoreRsp(command);
		}
	}
	
	/**
	 * 构造dev 控制命令
	 * payload -->符合设备控制模型的json字符串： {"cmd":"cmdname", "paras":{"p1":"v1", "p2":"v2", ...}} 
	 */
	public CSIFCommand BuildDevCtrlCmd(String appEui, String devEui, boolean confirm, int port, String payload, Map<String, Object> extParaMap) throws Exception {
		logger.info("BuildDevCtrlCmd: appEUI="+appEui+", payload="+payload);
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if(ccs == null){
			logger.error("[*@@*]AppEUI not exist in cur CSA module: "+appEui);
			throw new Exception("AppEUI not exist in cur CSA module: "+appEui);
		}
		// 1.调用js插件针对payload进行命令参数转换...
		DevBase dev = this.cacheManager.getDev(devEui);
		if(dev == null){
			logger.error("[*@@*]Dev not exist in local cache/db: "+devEui);
			throw new Exception("Dev not exist in local cache/db: "+devEui);
		}
		String devTypeId = dev.getDevType();
		Invocable jsRunner = this.jsPnpService.getJsPLugin(devTypeId);
		if(jsRunner == null){
			logger.error("[*!!*]Can not find the JS plugin for devtype="+devTypeId);
			throw new Exception("Can not find the JS plugin for devtype="+devTypeId);
		}		
		JSONObject json = new JSONObject(payload);
		String cmdCode = JsonUtils.getJsonAttrVal(json, "cmd", null);
		if(cmdCode == null){
			logger.error("[*@@*]Can not get cmd code from payload[cmd]: "+payload);
			throw new Exception("Can not get cmd code from payload[cmd]: "+payload);
		}
		Map<String, Object> cmdParaMap = null;
		JSONObject jsonParaObj = JsonUtils.getSubObject(json, "paras");
		if(jsonParaObj == null){
			cmdParaMap = new HashMap<String, Object>(0);
		} else {
			cmdParaMap = JsonUtils.jsonStrToHashMap(jsonParaObj.toString());
		}
		// 检查并尝试根据init-cmd的初始化命令补充sensor的sendto命令的slotid参数，前提是initcmd必须配置. 
		this.checkAppendSlotid4SensorDev(dev, cmdParaMap);
		// 针对modbus类的sensor设备，补充addr地址参数.
		this.checkAppendModbusAddr4SensorDev(dev, cmdParaMap);
		JSParam jsParam = new JSParam();
		jsParam.setDeveui(devEui);
		jsParam.setDevtypeid(devTypeId);
		jsParam.setDevport(""+port);
		jsParam.setParas(cmdParaMap);
		if(extParaMap != null){
			jsParam.setDevExtDatas(extParaMap);
		}
		// 为了兼容GSP/SSP设备，方便js插件内部获取sensorid，这里尝试解析一下sensorid（-1无效）
		try{
			String sensorId = dev.parseSensorId();
			if(sensorId != null && !sensorId.equals("")){
				jsParam.setSensorid(Integer.parseInt(sensorId));
			}
		}catch(Throwable ignore){}
		byte[] pbs = (byte[])jsRunner.invokeFunction(cmdCode, jsParam);	
		if(pbs == null || pbs.length == 0){
			throw new Exception("下发命令构造错误，请检查命令参数设置！");
		}
		// 如果是非lora的传感终端，这里需要使用dtueui
		return CSIFCmdBuilder.buildDevCtrlCmd(dev.parseDtuDevEui(), confirm, pbs, port);
	}
	
	public GPRSDevCommand BuildGPRSDevCtrlCmd(String appEui, String devEui, boolean confirm, int port, String payload, Map<String, Object> extParaMap) throws Exception {
		logger.info("BuildGPRSDevCtrlCmd: appEUI="+appEui+", payload="+payload);

		// 1.调用js插件针对payload进行命令参数转换...
		DevBase dev = this.cacheManager.getDev(devEui);
		if(dev == null){
			logger.error("[*@@*]Dev not exist in local cache/db: "+devEui);
			throw new Exception("Dev not exist in local cache/db: "+devEui);
		}
		String devTypeId = dev.getDevType();
		Invocable jsRunner = this.jsPnpService.getJsPLugin(devTypeId);
		if(jsRunner == null){
			logger.error("[*!!*]Can not find the JS plugin for devtype="+devTypeId);
			throw new Exception("Can not find the JS plugin for devtype="+devTypeId);
		}		
		JSONObject json = new JSONObject(payload);
		String cmdCode = JsonUtils.getJsonAttrVal(json, "cmd", null);
		if(cmdCode == null){
			logger.error("[*@@*]Can not get cmd code from payload[cmd]: "+payload);
			throw new Exception("Can not get cmd code from payload[cmd]: "+payload);
		}
		Map<String, Object> cmdParaMap = null;
		JSONObject jsonParaObj = JsonUtils.getSubObject(json, "paras");
		if(jsonParaObj == null){
			cmdParaMap = new HashMap<String, Object>(0);
		} else {
			cmdParaMap = JsonUtils.jsonStrToHashMap(jsonParaObj.toString());
		}
		// 检查并尝试根据init-cmd的初始化命令补充sensor的sendto命令的slotid参数，前提是initcmd必须配置. 
		this.checkAppendSlotid4SensorDev(dev, cmdParaMap);
		// 针对modbus类的sensor设备，补充addr地址参数.
		this.checkAppendModbusAddr4SensorDev(dev, cmdParaMap);
		JSParam jsParam = new JSParam();
		jsParam.setDeveui(devEui);
		jsParam.setDevtypeid(devTypeId);
		jsParam.setDevport(""+port);
		jsParam.setParas(cmdParaMap);
		if(extParaMap != null){
			jsParam.setDevExtDatas(extParaMap);
		}
		// 为了兼容GSP/SSP设备，方便js插件内部获取sensorid，这里尝试解析一下sensorid（-1无效）
		try{
			String sensorId = dev.parseSensorId();
			if(sensorId != null && !sensorId.equals("")){
				jsParam.setSensorid(Integer.parseInt(sensorId));
			}
		}catch(Throwable ignore){}
		byte[] pbs = (byte[])jsRunner.invokeFunction(cmdCode, jsParam);	
		if(pbs == null || pbs.length == 0){
			throw new Exception("下发命令构造错误，请检查命令参数设置！");
		}
		
		GPRSDevCommand gprscmd = new GPRSDevCommand();
		gprscmd.setDeveui(devEui);
		byte[] gprsPbs = new byte[pbs.length+3];
		gprsPbs[0] = (byte)GPRSDevCommand.GPRS_DOWNLINK;
		gprsPbs[1] = (byte)((pbs.length >> 8) & 0xFF);
		gprsPbs[2] = (byte)(pbs.length & 0xFF);
		for(int i=0; i<pbs.length; i++){
			gprsPbs[i+3] = pbs[i];
		}
		gprscmd.setPayload(gprsPbs);
		// 如果是非lora的传感终端，这里需要使用dtueui
		return gprscmd;
	}
	
	public com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSDevCommand BuildGPRSCat1DevCtrlCmd(String appEui, String devEui, boolean confirm, int port, String payload, Map<String, Object> extParaMap) throws Exception {
		logger.info("BuildGPRSCat1DevCtrlCmd: appEUI="+appEui+", payload="+payload);

		// 1.调用js插件针对payload进行命令参数转换...
		DevBase dev = this.cacheManager.getDev(devEui);
		if(dev == null){
			logger.error("[*@@*]Dev not exist in local cache/db: "+devEui);
			throw new Exception("Dev not exist in local cache/db: "+devEui);
		}
		String devTypeId = dev.getDevType();
		Invocable jsRunner = this.jsPnpService.getJsPLugin(devTypeId);
		if(jsRunner == null){
			logger.error("[*!!*]Can not find the JS plugin for devtype="+devTypeId);
			throw new Exception("Can not find the JS plugin for devtype="+devTypeId);
		}		
		JSONObject json = new JSONObject(payload);
		String cmdCode = JsonUtils.getJsonAttrVal(json, "cmd", null);
		if(cmdCode == null){
			logger.error("[*@@*]Can not get cmd code from payload[cmd]: "+payload);
			throw new Exception("Can not get cmd code from payload[cmd]: "+payload);
		}
		Map<String, Object> cmdParaMap = null;
		JSONObject jsonParaObj = JsonUtils.getSubObject(json, "paras");
		if(jsonParaObj == null){
			cmdParaMap = new HashMap<String, Object>(0);
		} else {
			cmdParaMap = JsonUtils.jsonStrToHashMap(jsonParaObj.toString());
		}
		// 检查并尝试根据init-cmd的初始化命令补充sensor的sendto命令的slotid参数，前提是initcmd必须配置. 
		this.checkAppendSlotid4SensorDev(dev, cmdParaMap);
		// 针对modbus类的sensor设备，补充addr地址参数.
		this.checkAppendModbusAddr4SensorDev(dev, cmdParaMap);
		JSParam jsParam = new JSParam();
		jsParam.setDeveui(devEui);
		jsParam.setDevtypeid(devTypeId);
		jsParam.setDevport(""+port);
		jsParam.setParas(cmdParaMap);
		if(extParaMap != null){
			jsParam.setDevExtDatas(extParaMap);
		}
		// 为了兼容GSP/SSP设备，方便js插件内部获取sensorid，这里尝试解析一下sensorid（-1无效）
		try{
			String sensorId = dev.parseSensorId();
			if(sensorId != null && !sensorId.equals("")){
				jsParam.setSensorid(Integer.parseInt(sensorId));
			}
		}catch(Throwable ignore){}
		byte[] pbs = (byte[])jsRunner.invokeFunction(cmdCode, jsParam);	
		if(pbs == null || pbs.length == 0){
			throw new Exception("下发命令构造错误，请检查命令参数设置！");
		}
		com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSDevCommand gprscat1cmd = new com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSDevCommand();
//		int cmdtype = com.zte.claa.inficombo.csa.app.service.gprscat1.bean.GPRSMsgHead.GPRSCAT1_DW_HBRSP;
		String cmdpayload = new String(pbs);
		JSONObject cmdjson = new JSONObject(cmdpayload);
		String dtstr = JsonUtils.getJsonAttrVal(cmdjson, "dt", null);
		if(dtstr == null){
			logger.error("[*@@*]Can not get dt from cmdpayload[cmd]: "+cmdpayload);
			throw new Exception("Can not get dt from cmdpayload[cmd]: "+cmdpayload);
		}
		gprscat1cmd.setDeveui(devEui);
		gprscat1cmd.setCmdtype(Integer.parseInt(dtstr));
		gprscat1cmd.setPaylen(pbs.length);
		gprscat1cmd.setPayload(pbs);
		// 如果是非lora的传感终端，这里需要使用dtueui
		return gprscat1cmd;
	}
	
	/**
	 * 针对sensor dev，如果没有设置slotid参数，则查询初始化命令，补充slotid参数。
	 * @param dev
	 * @param cmdParaMap
	 */
	private void checkAppendSlotid4SensorDev(final DevBase dev, Map<String, Object> cmdParaMap) {
		try{
			// 仅针对sensor dev...
			if(dev.checkIsSensor() == false){
				return;
			}
			Object slotIdObj = cmdParaMap.get(CMD_PARA_SLOTID);
			if(slotIdObj != null && !slotIdObj.toString().equals("")){
				logger.info("slotid para already set in cmd para: "+slotIdObj);
				return;
			}
			// 否则，根据初始化命令补充slotid参数
			List<String> cmdJsonArr = this.devCmdConfMapper.queryDevCmdConfJsonData(dev.getDevEui());
			if(cmdJsonArr == null || cmdJsonArr.size() == 0){
				logger.info("There is no VALID init-cmd config data for dev: "+dev.getDevEui());
				return;
			}
			String cmdJsonStr = cmdJsonArr.get(0);
			logger.info("checkAndAppendSlotidForSensorDev: init-cmdjson="+cmdJsonStr);
			JSONObject json = new JSONObject(cmdJsonStr);
			JSONObject jsonParaObj = JsonUtils.getSubObject(json, "paras");
			if(jsonParaObj == null){
				logger.warn("init cmd paras is null!");
				return;
			}
			Map<String, Object> tmpMap = JsonUtils.jsonStrToHashMap(jsonParaObj.toString());
			slotIdObj = tmpMap.get(CMD_PARA_SLOTID);
			if(slotIdObj != null){
				cmdParaMap.put(CMD_PARA_SLOTID, slotIdObj);
				logger.info("[@@]Set slotid para for sensor cmd ok: slotid="+slotIdObj+", deveui="+dev.getDevEui());
			}	
		}catch(Exception e){
			logger.warn("[!!]checkAndAppendSlotidForSensorDev failed: "+dev, e);
		}
	}
	
	/**
	 * 针对sensor dev，如果没有设置addr参数，则查询初始化命令，补充addr参数。
	 * @param dev
	 * @param cmdParaMap
	 */
	private void checkAppendModbusAddr4SensorDev(final DevBase dev, Map<String, Object> cmdParaMap) {
		try{
			// 仅针对sensor dev...
			if(dev.checkIsSensor() == false){
				return;
			}
			Object addrObj = cmdParaMap.get(CMD_PARA_ADDR);
			if(addrObj != null && !addrObj.toString().equals("")){
				logger.info("register addr para already set in cmd para: "+addrObj);
				return;
			}
			// 否则，根据初始化命令补充addr参数
			List<String> cmdJsonArr = this.devCmdConfMapper.queryDevCmdConfJsonData(dev.getDevEui());
			if(cmdJsonArr == null || cmdJsonArr.size() == 0){
				logger.info("There is no VALID init-cmd config data for dev: "+dev.getDevEui());
				return;
			}
			String cmdJsonStr = cmdJsonArr.get(0);
			logger.info("checkAndAppendSlotidForSensorDev: init-cmdjson="+cmdJsonStr);
			JSONObject json = new JSONObject(cmdJsonStr);
			JSONObject jsonParaObj = JsonUtils.getSubObject(json, "paras");
			if(jsonParaObj == null){
				logger.warn("init cmd paras is null!");
				return;
			}
			Map<String, Object> tmpMap = JsonUtils.jsonStrToHashMap(jsonParaObj.toString());
			addrObj = tmpMap.get(CMD_PARA_ADDR);
			if(addrObj != null){
				cmdParaMap.put(CMD_PARA_ADDR, addrObj);
				logger.info("[@@]Set register addr para for sensor cmd ok: addr="+addrObj+", deveui="+dev.getDevEui());
			}	
		}catch(Exception e){
			logger.warn("[!!]checkAppendModbusAddr4SensorDev failed: "+dev, e);
		}
	}

	/**
	 * 设备的下行指令buff缓存状况查询
	 */
	@Override
	public String DevBuffQuery(String appEui, String devEui) throws Exception {
		logger.info("DevBuffQuery: appEUI="+appEui);
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if(ccs == null){
			throw new Exception("AppEUI not exist in cur CSA module: "+appEui);
		}
		DevBase dev = this.cacheManager.getDev(devEui);
		if(dev == null){
			logger.error("[*@@*]Dev not exist in local cache/db: "+devEui);
			throw new Exception("Dev not exist in local cache/db: "+devEui);
		}
		CSIFCommand command = CSIFCmdBuilder.buildDevBuffQueryCmd(dev.parseDtuDevEui());
		return ccs.execCommand(command);
	}

	/**
	 * 设备类型查询
	 * 
	 */
	@Override
	public String DevClassQuery(String appEui, String devEui) throws Exception {
		logger.info("DevClassQuery: appEUI="+appEui);
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if(ccs == null){
			throw new Exception("AppEUI not exist in cur CSA module: "+appEui);
		}
		DevBase dev = this.cacheManager.getDev(devEui);
		if(dev == null){
			logger.error("[*@@*]Dev not exist in local cache/db: "+devEui);
			throw new Exception("Dev not exist in local cache/db: "+devEui);
		}
		CSIFCommand command = CSIFCmdBuilder.buildDevClassQueryCmd(dev.parseDtuDevEui());
		return ccs.execCommand(command);
	}

	/**
	 * 设备状态查询
	 * 
	 */
	@Override
	public String DevStateQuery(String appEui, String devEui) throws Exception {
		logger.info("DevStateQuery: appEUI="+appEui);
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if(ccs == null){
			throw new Exception("AppEUI not exist in cur CSA module: "+appEui);
		}
		DevBase dev = this.cacheManager.getDev(devEui);
		if(dev == null){
			logger.error("[*@@*]Dev not exist in local cache/db: "+devEui);
			throw new Exception("Dev not exist in local cache/db: "+devEui);
		}
		CSIFCommand command = CSIFCmdBuilder.buildDevStateQueryCmd(dev.parseDtuDevEui());
		return ccs.execCommand(command);
	}

	/**
	 * 广播命令下发
	 * 
	 * payload -->符合设备控制模型的json字符串： {"cmd":"cmdname", "dr":0, "ch":25, "paras":{"p1":"v1", "p2":"v2", ...}} 
	 */
	@Override
	public String SendBcastCmd(String appEui, int port, int times, String devTypeId, String payload, Map<String, Object> extParaMap) throws Exception {
		logger.info("SendBcastCmd: appEUI="+appEui+", payload="+payload);
		/*
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if(ccs == null){
			logger.error("[*@@*]AppEUI not exist in cur CSA module: "+appEui);
			throw new Exception("AppEUI not exist in cur CSA module: "+appEui);
		}
		*/
		Invocable jsRunner = this.jsPnpService.getJsPLugin(devTypeId);
		if(jsRunner == null){
			logger.error("[*!!*]Can not find the JS plugin for devtype="+devTypeId);
			throw new Exception("Can not find the JS plugin for devtype="+devTypeId);
		}
		
		JSONObject json = new JSONObject(payload);
		String cmdCode = JsonUtils.getJsonAttrVal(json, "cmd", null);
		if(cmdCode == null){
			logger.error("[*@@*]Can not get cmd code from payload[cmd]: "+payload);
			throw new Exception("Can not get cmd code from payload[cmd]: "+payload);
		}		
		// 自研设备升级时会用到dr，其他情况下不涉及.
		String drStr = JsonUtils.getJsonAttrVal(json, "dr", "-1");
		if(drStr.trim().equals("")){
			drStr = "-1";
		}
		int dr = Integer.parseInt(drStr);
		// 自研设备升级时会用到ch，其他情况下不涉及.
		String chStr = JsonUtils.getJsonAttrVal(json, "ch", "-1");
		if(chStr.trim().equals("")){
			chStr = "-1";
		}
		int ch = Integer.parseInt(chStr);
		// 自研设备版本号
		String updatever = JsonUtils.getJsonAttrVal(json, "version", "");
		if(updatever.trim().equals("")){
			updatever = "";
		}		
		JSONObject jsonParaObj = JsonUtils.getSubObject(json, "paras");
		Map<String, Object> cmdParaMap = JsonUtils.jsonStrToHashMap(jsonParaObj.toString());		
		JSParam jsParam = new JSParam();
		jsParam.setDeveui("");
		jsParam.setDevtypeid(devTypeId);
		jsParam.setDevport(""+port);
		jsParam.setParas(cmdParaMap);
		if(extParaMap != null){
			jsParam.setDevExtDatas(extParaMap);
		}
	
		String retStr = "";
		// LoRa场景需要判断是否存在与appeui对应的CSIF连接，如果存在才发送广播命令
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if(ccs != null){
			byte[] pbs = (byte[])jsRunner.invokeFunction(cmdCode, jsParam);
			if(pbs == null || pbs.length == 0){
				throw new Exception("广播命令构造错误，请检查命令参数设置！");
			}
			CSIFCommand command = CSIFCmdBuilder.buildBcastCmd(pbs, port, times, dr, ch);
			retStr = ccs.execCommand(command);
		}
		
		// GPRS场景需要考虑广播转单播
		try{			
    		DevPagePara pp = new DevPagePara();
    		pp.setBatch(5000);
    		int pageindex = 1;
    		pp.setPageIndex(pageindex);
			List<DevBase> devList = this.devBaseService.queryDevsByAppeui(pp, appEui);
			if(null != devList){
				logger.info("GPRS devices is updating version, devlist size:"+ devList.size());
			}			
			boolean hasMore = (devList != null && devList.size() > 0);	
			List<String> dtuExistList = new ArrayList<String>();
			while(hasMore){				
				for(DevBase devbase : devList){		
					String dtueui = devbase.parseDtuDevEui();
					GPRSDevConnSession gprssession = this.gprsDevService.getConnSvrManager().getDeveuiSessionMap().get(dtueui);
					//if(!dtuExistList.contains(dtueui) && null != gprssession && !gprssession.isUpdatefinished()){
					if(!dtuExistList.contains(dtueui) && null != gprssession){
						ClaaDevInitState devinitstat = devInitStateMapper.queryClaaDevInitState(dtueui);
						String devicever = null;
						if(null != devinitstat){
							devicever = devinitstat.getVersion();
						}
						logger.info("GPRS device version compare, dtueui:"+dtueui+", devicever:"+devicever+", updatever:"+updatever);
						if(null != devicever && null != updatever && devicever.equals(updatever)){	
							//gprssession.setUpdatefinished(true);
							logger.info("GPRS device version is same as updating version!");
							continue;
						}
						//处理GPRS侧命令	
						logger.info("GPRS change bcast command to device cmd: dtueui="+dtueui+",sensorid="+devbase.getDevEui());
						boolean confirm = false;
						GPRSDevCommand gprscmd = this.BuildGPRSDevCtrlCmd(appEui, dtueui, confirm, port, payload, extParaMap);
						gprscmd.setCmdtype((byte)GPRSDevCommand.GPRS_DOWNLINK);			
						this.gprsDevService.sendCommand(dtueui, gprscmd);
						dtuExistList.add(dtueui);
					}
				}
				pageindex++;
				pp.setPageIndex(pageindex);
				devList = this.devBaseService.queryDevsByAppeui(pp,appEui);
				hasMore = (devList != null && devList.size() > 0);
			}
		}catch(Exception e){
			logger.warn("[!!]GPRS SendBcastCmd failed: ", e);
		}
		retStr = "{\"code\":200}";
		return retStr;
	}	
	
	
	/**
	 * 组播密钥查询
	 * 
	 * @param appEui
	 * @return
	 * @throws Exception
	 */
	public String BcastQueueQuery(String appEui) throws Exception {
		logger.info("BcastQueueQuery: appEUI="+appEui);
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if(ccs == null){
			throw new Exception("AppEUI not exist in cur CSA module: "+appEui);
		}
		CSIFCommand command = CSIFCmdBuilder.buildBcastQueueQueryCmd(appEui);
		return ccs.execCommand(command);
	}
	
	// ------------------------- CSIF DEV CRUD -------------------// 
	/**
	 * 添加dev
	 * appkey = null || "" 表示不设置；
	 * clazz = null || "" 表示A类型；
	 * offlineTime <= 0 表示不设置（使用LNS默认值）
	 */
	public String addDev(String appEui, String devEui, String appkey, String clazz, int offlineTime) throws Exception{
		logger.info("addDev: devEui="+devEui);
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if(ccs == null){
			throw new Exception("AppEUI not exist in cur CSA module: "+appEui);
		}
		CSIFCommand command = CSIFCmdBuilder.buildDevAddCmd(devEui, appkey, clazz, offlineTime);
		return ccs.execCommand(command);
	}
	
	/**
	 * 删除dev
	 */
	public String removeDev(String appEui, String devEui) throws Exception{
		logger.info("removeDev: devEui="+devEui);
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if(ccs == null){
			throw new Exception("AppEUI not exist in cur CSA module: "+appEui);
		}
		CSIFCommand command = CSIFCmdBuilder.buildDevDelCmd(devEui);
		return ccs.execCommand(command);
	}

	/**
	 * 修改dev
	 */
	public String modifyDev(String appEui, String devEui, int offlineTime) throws Exception{
		logger.info("removeDev: devEui="+devEui);
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if(ccs == null){
			throw new Exception("AppEUI not exist in cur CSA module: "+appEui);
		}
		CSIFCommand command = CSIFCmdBuilder.buildDevSetCmd(devEui, offlineTime);
		return ccs.execCommand(command);
	}
	
	/**
	 * 查询dev
	 */
	public String queryDev(String appEui, String devEui) throws Exception{
		logger.info("removeDev: devEui="+devEui);
		CSIFCommService ccs = this.appeui2CSIFMap.get(appEui);
		if(ccs == null){
			throw new Exception("AppEUI not exist in cur CSA module: "+appEui);
		}
		CSIFCommand command = CSIFCmdBuilder.buildDevQueCmd(devEui);
		return ccs.execCommand(command);
	}
	
}
